package com.example.palette.view;

import static androidx.customview.widget.ViewDragHelper.STATE_IDLE;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.core.view.ViewCompat;
import androidx.customview.widget.ViewDragHelper;

public class VerticalDrawerLayout extends ViewGroup {
    private View mMainView;
    private View mDrawerView;
    private ViewDragHelper mViewDragHelper;

    private int mDirection = 1;

    private int mCurrentViewDragState;

    private DrawListener mDrawListener;

    private float mCurrentDensity;

    public VerticalDrawerLayout(Context context) {
        this(context, null);
    }

    public VerticalDrawerLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public VerticalDrawerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mViewDragHelper = ViewDragHelper.create(this, new ViewDragHelper.Callback() {
            @Override
            public boolean tryCaptureView(@NonNull View child, int pointerId) {
                return child == mDrawerView;
            }

            @Override
            public void onViewPositionChanged(@NonNull View changedView, int left, int top, int dx, int dy) {
                int childHeight = changedView.getHeight();
                float offset = 1 - ((float) (-top) / (childHeight));
                CustomLayoutParams layoutParams = (CustomLayoutParams) changedView.getLayoutParams();
                if (offset != layoutParams.mDensity) {
                    layoutParams.mDensity = offset;
                    if (mDrawListener != null) {
                        mDrawListener.onViewPositionChanged(changedView, offset);
                    }
                }
            }

            @Override
            public void onViewDragStateChanged(int state) {
                View drawerView = mViewDragHelper.getCapturedView();
                int viewDragState = mViewDragHelper.getViewDragState();
                if (drawerView != null && state == STATE_IDLE) {
                    CustomLayoutParams layoutParams = (CustomLayoutParams) drawerView.getLayoutParams();
                    if (layoutParams.mDensity == 0) {
                        if (layoutParams.mDrawerOpen) {
                            layoutParams.mDrawerOpen = false;
                            if (mDrawListener != null) {
                                mDrawListener.onDrawerClosed(drawerView);
                            }
                        }
                    } else if (layoutParams.mDensity == 1) {
                        if (!layoutParams.mDrawerOpen) {
                            layoutParams.mDrawerOpen = true;
                            if (mDrawListener != null) {
                                mDrawListener.onDrawerOpened(drawerView);
                            }
                        }
                    }
                }
                if (viewDragState != mCurrentViewDragState) {
                    mCurrentViewDragState = viewDragState;
                    if (mDrawListener != null) {
                        mDrawListener.onViewDragStateChanged(viewDragState);
                    }
                }
            }
        });
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (getChildCount() != 2) {
            throw new IllegalArgumentException("too many child views");
        }
        mMainView = getChildAt(0);
        mDrawerView = getChildAt(1);
    }

    private boolean isMainView(View view) {
        return view == mMainView;
    }

    public void setDrawListener(DrawListener listener) {
        mDrawListener = listener;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int selfWidth = MeasureSpec.getSize(widthMeasureSpec);
        int selfHeight = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(selfWidth, selfHeight);
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            if (child.getVisibility() == GONE) {
                continue;
            }
            try {
                MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams();
                int childWidth = getChildMeasureSpec(widthMeasureSpec, layoutParams.leftMargin + layoutParams.rightMargin, layoutParams.width);
                int childHeight = getChildMeasureSpec(heightMeasureSpec, layoutParams.topMargin + layoutParams.bottomMargin, layoutParams.height);
                child.measure(childWidth, childHeight);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            try {
                CustomLayoutParams layoutParams = (CustomLayoutParams) child.getLayoutParams();
                if (isMainView(child)) {
                    int left = layoutParams.leftMargin;
                    int top = layoutParams.topMargin;
                    int right = left + child.getMeasuredWidth();
                    int bottom = top + child.getMeasuredHeight();
                    child.layout(left, top, right, bottom);
                } else {
                    if (mDirection == 0) {
                        int left = layoutParams.leftMargin;
                        int right = left + child.getMeasuredWidth();
                        child.layout(left, getHeight(), right, getHeight() + child.getMeasuredHeight());
                    } else if (mDirection == 1) {
                        int left = layoutParams.leftMargin;
                        int right = left + child.getMeasuredWidth();
                        child.layout(left, -child.getMeasuredHeight(), right, 0);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        boolean shouldInterceptTouchEvent = mViewDragHelper.shouldInterceptTouchEvent(ev);
        boolean intercept = false;
        int action = ev.getActionMasked();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                final float x = ev.getX();
                final float y = ev.getY();
                final View touchedView = mViewDragHelper.findTopChildUnder((int) x, (int) y);
                if (isMainView(touchedView) && mCurrentDensity > 0) {
                    intercept = true;
                }
                break;
        }
        return shouldInterceptTouchEvent || intercept;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mViewDragHelper.processTouchEvent(event);
        int action = event.getAction();
        switch (action & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_MOVE:
                return false;
            case MotionEvent.ACTION_UP:
                final float x = event.getX();
                final float y = event.getY();
                final View touchedView = mViewDragHelper.findTopChildUnder((int) x, (int) y);
                if (touchedView == null) {
                    return false;
                }
                if (isMainView(touchedView) && mCurrentDensity > 0) {
                    closeDrawer();
                }
                break;
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_CANCEL:
                break;
        }
        return true;
    }

    public void openDrawer() {
        if (mDirection == 0) {
            mViewDragHelper.smoothSlideViewTo(mDrawerView, mDrawerView.getLeft(), getHeight() - mDrawerView.getMeasuredHeight());
        } else if (mDirection == 1) {
            mViewDragHelper.smoothSlideViewTo(mDrawerView, mDrawerView.getLeft(), 0);
        }
        invalidate();
    }

    public void closeDrawer() {
        if (mDirection == 0) {
            mViewDragHelper.smoothSlideViewTo(mDrawerView, mDrawerView.getLeft(), getHeight());
        } else if (mDirection == 1) {
            mViewDragHelper.smoothSlideViewTo(mDrawerView, mDrawerView.getLeft(), -mDrawerView.getMeasuredHeight());
        }
        invalidate();
    }

    @Override
    public void computeScroll() {
        float max = 0;
        float density = ((CustomLayoutParams) getChildAt(1).getLayoutParams()).mDensity;
        max = Math.max(max, density);
        mCurrentDensity = max;
        if (mViewDragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new CustomLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    }

    @Override
    protected LayoutParams generateLayoutParams(LayoutParams p) {
        return p instanceof CustomLayoutParams ?
                new CustomLayoutParams(p) :
                p instanceof MarginLayoutParams ?
                        new CustomLayoutParams((MarginLayoutParams) p) :
                        new CustomLayoutParams(p);

    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new CustomLayoutParams(getContext(), attrs);
    }

    public static class CustomLayoutParams extends MarginLayoutParams {
        boolean mDrawerOpen;
        float mDensity;

        public CustomLayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
        }

        public CustomLayoutParams(int width, int height) {
            super(width, height);
        }

        public CustomLayoutParams(MarginLayoutParams source) {
            super(source);
        }

        public CustomLayoutParams(LayoutParams source) {
            super(source);
        }
    }

    public interface DrawListener {
        void onViewPositionChanged(View drawerView, float density);

        void onViewDragStateChanged(int newState);

        void onDrawerOpened(View drawerView);

        void onDrawerClosed(View drawerView);
    }
}
